#include "baijiViewer.h"

using namespace std;
using namespace MeshUtils;

Viewer::~Viewer(void)
{
	delete imageLoader;
	delete input;
	
	delete top;
	delete gui;
	delete font;
	delete console;
}

Viewer::Viewer(unsigned int w, unsigned int h) : width(w), height(h), xMin(0.0), yMin(0.0), xMax(0.0), yMax(0.0), isActive(true), mode(OBJECT), isRunning(true)
{
	ambientLight[0] = 1.0;
	ambientLight[1] =1.0;
	ambientLight[2] = 1.0;
	ambientLight[3] = 1.0;

	diffuseLight[0] = 1.0;
	diffuseLight[1] = 1.0;
	diffuseLight[2] = 1.0;
	diffuseLight[3] = 1.0;

	positionLight[0] = 1.0;
	positionLight[1] = 0.0;
	positionLight[2] = 0.0;
	positionLight[3] = 0.0;

	lampPosition[0] = 0.0;
	lampPosition[1] = 0.0;
	lampPosition[2] = 50.0;
	lampPosition[3] = 1.0;

	SDL_Init(SDL_INIT_VIDEO);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	
	screen = SDL_SetVideoMode(w, h, 32, SDL_HWSURFACE | SDL_OPENGL | SDL_HWACCEL);
	
	// We want unicode
	SDL_EnableUNICODE(1);
	// We want to enable key repeat
	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
	
	/*
	* Now it's time for Guichan OpenGL/SDL stuff
	*/
	imageLoader = new gcn::OpenGLSDLImageLoader();
	
	// The ImageLoader in use is static and must be set to be
	// able to load images
	gcn::Image::setImageLoader(imageLoader);
	graphics = new gcn::OpenGLGraphics();
	// We need to tell OpenGL graphics how big the screen is.
	graphics->setTargetPlane(w, h);
	input = new gcn::SDLInput();
	
	/*
	* Last but not least it's time to initialize and create the gui
	* with Guichan stuff.
	*/
	top = new gcn::Container();
	top->setOpaque(false);
	// Set the dimension of the top container to match the screen.
	
	top->setDimension(gcn::Rectangle(0, 0, w, h));
	gui = new gcn::Gui();
	// Set gui to use the SDLGraphics object.
	gui->setGraphics(graphics);
	// Set gui to use the SDLInput object
	gui->setInput(input);
	// Set the top container
	gui->setTop(top);
	// Load the image font.
	//font = new gcn::ImageFont( "techyfontbig.png"," abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`'*#=[]\"");
	font = new gcn::ImageFont("rpgfont.png", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`'*#=[]\"");
	// The global font is static and must be set.
	gcn::Widget::setGlobalFont(font);

	ConsoleActionListener* consoleActionListener = new ConsoleActionListener();
	console = new Console();
	//Add console to BaijiAccesor to use it from lua.
    //Theses init lust be done before console->init
	BaijiAccessor::setConsole(*console);
	BaijiAccessor::setMeshManager(mshMng);
    console->init();

	console->addKeyListener ( consoleActionListener );
	top->add(console);
	
	//graphics->drawRectangle(gcn::Rectangle(0, 0, 250, 250));
	
	this->initGl();
}



void Viewer::perspective(double fov, double aspect, double zNear, double zFar)
{
	double pi = 4.0 * atan(1.0);
	double fH = tan (fov / 360 * pi) * zNear;
	double fW = fH * aspect;
	glFrustum(-fW, fW, -fH, fH, zNear, zFar);
	xMin = -fW;
	xMax = fW;
	yMin = -fH;
	yMax = fH;
}

void Viewer::drawTriangles(ptToSetMat setMatp, std::vector<Triangle>& tList)
{
	for(vector<Triangle>::iterator pIt = tList.begin() ; pIt != tList.end() ; pIt++)
	{
		glNormal3f(0.0, 0.0, 1.0);
		(*setMatp)(NULL);
		glVertex3f(pIt->a.x, pIt->a.y, pIt->a.z);
		glVertex3f(pIt->b.x, pIt->b.y, pIt->b.z);
		glVertex3f(pIt->c.x, pIt->c.y, pIt->c.z);
	}
}

void Viewer::setMat(void*)
{};

void drawAux(Viewer* This, SceneNode& root, SceneManager& scnMng, MeshManager& mshMng)
{
	string entName = root.getEntityName();
	Entity& ent = scnMng.getEntity(entName);
	Mesh& mesh = mshMng.getMesh(ent.meshName);
	glPushMatrix();
	glTranslatef(root.getPosition().x, root.getPosition().y, root.getPosition().z);
	glBegin(GL_TRIANGLES);
	This->drawTriangles(&(Viewer::setMat), mesh.triangles);
	glEnd();
	glPopMatrix();

	for(std::vector<SceneNode*>::iterator cIt = root.childs.begin() ; cIt != root.childs.end() ; cIt++)
		drawAux(This, *(*cIt), scnMng, mshMng);
}

void Viewer::drawScene(ptToSetMat setMatp)
{
	drawAux(this, scnMng.getRoot(), scnMng, mshMng);
}

void Viewer::drawPoints(ptToSetMat setMatp, std::vector<MeshUtils::Vector3>& ptList, void* setMatValue)
{
	unsigned int i = 0 ;
	for(vector<MeshUtils::Vector3>::iterator pIt = ptList.begin() ; pIt != ptList.end() ; pIt++)
	{
		glNormal3f(0.0, 0.0, 1.0);
		(*setMatp)(setMatValue);
		glVertex3f(pIt->x, pIt->y, pIt->z);
		i++;
	}
}

void drawAuxPoints(Viewer* This, SceneNode& root, SceneManager& scnMng, MeshManager& mshMng, ptToSetMat setMatp, void* setMatValue )
{
	string entName = root.getEntityName();
	Entity& ent = scnMng.getEntity(entName);
	Mesh& mesh = mshMng.getMesh(ent.meshName);

	glPushMatrix();
	glTranslatef(root.getPosition().x, root.getPosition().y, root.getPosition().z);
	glBegin(GL_POINTS);
	This->drawPoints(setMatp, mesh.nodes, setMatValue);
	glEnd();
	glPopMatrix();

	for(std::vector<SceneNode*>::iterator cIt = root.childs.begin() ; cIt != root.childs.end() ; cIt++)
		drawAuxPoints(This, *(*cIt), scnMng, mshMng, setMatp, setMatValue);
}
void Viewer::drawScenePoints(ptToSetMat setMatp, void* setMatValue)
{
	drawAuxPoints(this, scnMng.getRoot(), scnMng, mshMng, setMatp, setMatValue);
}

void drawAuxSelection(Viewer* This, SceneNode& root, SceneManager& scnMng, MeshManager& mshMng)
{
	static int nameIdx = 0;

	string entName = root.getEntityName();
	Entity& ent = scnMng.getEntity(entName);
	Mesh& mesh = mshMng.getMesh(ent.meshName);
	
	glPushMatrix();
	glPushName(nameIdx);
	glTranslatef(root.getPosition().x, root.getPosition().y, root.getPosition().z);
	glBegin(GL_TRIANGLES);
	This->drawTriangles(&(Viewer::setMat), mesh.triangles);
	glEnd();
	glPopName ();
	glPopMatrix();

	for(std::vector<SceneNode*>::iterator cIt = root.childs.begin() ; cIt != root.childs.end() ; cIt++)
		drawAux(This, **cIt, scnMng, mshMng);

	//FIXME : nameIdx shoud be reset after display
}


void Viewer::drawSceneInSelectionMode(void)
{
	drawAuxSelection(this, scnMng.getRoot(), scnMng, mshMng);
}

void Viewer::loadGlTextures(void)
{
	SDL_Surface* bk = SDL_LoadBMP("textures/sky2_bk.bmp");
	SDL_Surface* fr = SDL_LoadBMP("textures/sky2_fr.bmp");
	SDL_Surface* dn = SDL_LoadBMP("textures/sky2_dn.bmp");
	SDL_Surface* up = SDL_LoadBMP("textures/sky2_up.bmp");
	SDL_Surface* lf = SDL_LoadBMP("textures/sky2_lf.bmp");
	SDL_Surface* rt = SDL_LoadBMP("textures/sky2_rt.bmp");
	
	texture[0] = 0;
	texture[1] = 0;
	texture[2] = 0;
	texture[3] = 0;
	texture[4] = 0;
	texture[5] = 0;
	
	//Back texture
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	//2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, bk->w, bk->h, 0, GL_RGB, GL_UNSIGNED_BYTE, bk->pixels);
	//front texture
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, fr->w, fr->h, 0, GL_RGB, GL_UNSIGNED_BYTE, fr->pixels);
	//Down texture
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, dn->w, dn->h, 0, GL_RGB, GL_UNSIGNED_BYTE, dn->pixels);

	//Up texture
	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, up->w, up->h, 0, GL_RGB, GL_UNSIGNED_BYTE, up->pixels);

	//left texture
	glBindTexture(GL_TEXTURE_2D, texture[4]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, lf->w, lf->h, 0, GL_RGB, GL_UNSIGNED_BYTE, lf->pixels);

	//right texture
	glBindTexture(GL_TEXTURE_2D, texture[5]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, rt->w, rt->h, 0, GL_RGB, GL_UNSIGNED_BYTE, rt->pixels);
}

void Viewer::loadSkyBox(void)
{
	glEnable(GL_TEXTURE_2D);
	double hh = 500.0;
	double ww = 500.0;
	double ll = ww;
	double x = 0.0 - hh * 0.5;
	double y = 0.0 - ww * 0.5;
	double z = 0. - hh * 0.5;

	glDisable(GL_DEPTH_TEST);
	glDepthMask(false);

	glBindTexture(GL_TEXTURE_2D, texture[0]);//BACK
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(0.0, 0.0); glVertex3f(x, y, z);
	glTexCoord2f(1.0, 0.0); glVertex3f( x + ww, y, z);
	glTexCoord2f(0.0, 1.0); glVertex3f(x ,y + hh, z);
	glTexCoord2f(1.0, 1.0); glVertex3f(x + ww, y + hh, z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D,texture[1]);//Front
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(1.0, 0.0); glVertex3f(x, y, z + ll);
	glTexCoord2f(1.0, 1.0); glVertex3f(x, y + hh, z + ll);
	glTexCoord2f(0.0, 0.0); glVertex3f(x + ww, y, z + ll);
	glTexCoord2f(0.0, 1.0); glVertex3f(x + ww, y + hh, z + ll);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texture[2]);//Bottom face
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(1.0, 0.0); glVertex3f(x, y, z);
	glTexCoord2f(1.0, 1.0); glVertex3f(x, y, z + ll);
	glTexCoord2f(0.0, 0.0); glVertex3f(x + ww, y, z);
	glTexCoord2f(0.0, 1.0); glVertex3f(x + ww, y, z + ll);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texture[3]);//Top
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(1.0, 1.0); glVertex3f(x, y + hh, z);
	glTexCoord2f(0.0, 1.0); glVertex3f(x + ww, y + hh, z);
	glTexCoord2f(1.0, 0.0); glVertex3f(x, y + hh, z + ll);
	glTexCoord2f(0.0, 0.0); glVertex3f(x + ww, y + hh, z + ll);
	glEnd ();

	glBindTexture(GL_TEXTURE_2D, texture[5]);//Right
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(0.0, 0.0); glVertex3f(x + ww, y, z);
	glTexCoord2f(1.0, 0.0); glVertex3f(x + ww, y, z + ll);
	glTexCoord2f(0.0, 1.0); glVertex3f(x + ww, y + hh, z);
	glTexCoord2f(1.0, 1.0); glVertex3f(x + ww, y + hh, z + ll);
	glEnd ();

	glBindTexture(GL_TEXTURE_2D, texture[5]) ; //Left
	glBegin(GL_TRIANGLE_STRIP);
	glTexCoord2f(1.0, 0.0); glVertex3f(x, y, z);
	glTexCoord2f(1.0, 1.0); glVertex3f(x, y + hh, z);
	glTexCoord2f(0.0, 0.0); glVertex3f(x, y, z + ll);
	glTexCoord2f(0.0, 1.0); glVertex3f(x, y + hh, z + ll);
	glEnd ();


	glDepthMask(true);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);
}

void Viewer::initGl(void)
{
	loadGlTextures();
	glViewport(0, 0, width, height);
	glClearColor(0.45, 0.45, 0.45, 0.0);
	glClearDepth(1.0);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);
	glMatrixMode(GL_PROJECTION);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glLineWidth(1.0);
	glPointSize(8.0);
	glLoadIdentity();
	double aspect = width / height;
	perspective(45.0, aspect, 0.1, 1000.0);
	glMatrixMode(GL_MODELVIEW);
}
void Viewer::pickMatrix(double xx, double yy, double dx, double dy, int* viewport)
{
	if( dx <= 0.0 || dy <= 0.0)
		return;
	double x = (viewport[2] - 2.0 * (xx - viewport[0])) / dx;
	double y =  (viewport[3] - 2.0 * (yy - viewport[1])) / dy;
	double z = 0.0;
	glTranslatef(x, y, z);
	glScalef(viewport[2] / dx, viewport[3] / dy, 1.0);
}
void Viewer::startPicking(int x, int y, int dx, int dy)
{
	glSelectBuffer(16, records);
	glRenderMode(GL_SELECT);
	glInitNames();

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	int viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);
	pickMatrix(x, viewport[3] - y, dx, dy, viewport);
	double aspect = width / height;
	perspective(45.0, aspect, 0.1, 1000.0);
	glMatrixMode(GL_MODELVIEW);
}

void Viewer::stopPicking(void)
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix ();
	glMatrixMode(GL_MODELVIEW);

	int hits = glRenderMode(GL_RENDER);
	if(hits)
	{
	cout << "some object have been picked" << endl;
	cout << hits;
	cout << " to be precise ! This one " << endl;
	cout <<  records[3];
	cout << "to be even more precise" << endl;
	}
}
void Viewer::displayInSelectionMode(void)
{
	drawSceneInSelectionMode();
}
void Viewer::pickObject(int x, int y)
{
	startPicking(x, y, 10, 10);
	displayInSelectionMode();
	stopPicking() ;
}

void Viewer::pickEdit(int xx, int yy)
{
	glClearColor(1.0, 1.0, 1.0, 0.0);//clear color must be set to 255 255 255 (or 1.0 1.0 1.0) to discriminate background picking
	glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT);
	glLoadIdentity  () ; //Reset The View

	double x = camPosMove.x;
	double y = camPosMove.y;
	double z = camPosMove.z;
	glTranslatef(x, y, z);
	x = camPos.x;
	y = camPos.y;
	z = camPos.z;
	glTranslatef(x, y, z);

	Vector3 axis = camQuatMove.log();
	glRotatef( axis.norm() * 180.0 / 3.1415, axis.x, axis.y, axis.z);

	axis = camQuat.log();
	glRotatef(axis.norm() * 180.0 / 3.1415, axis.x, axis.y, axis.z);

	glDisable(GL_DITHER);
	glDisable(GL_TEXTURE_2D);

	glPushMatrix();
	glEnable(GL_COLOR_MATERIAL);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	unsigned int count = 0;
	unsigned int wPick = 6;
	unsigned int hPick = 6; 
	
	//setMat (dum:string) () = glColor3ub ((!count land 0xFF0000) lsr 16) ((!count land 0xFF00)  lsr 8) (!count land 0xFF) ; count := !count + 1 

	unsigned int idx = 0;
	drawScenePoints(&(Viewer::setMatForPicking), &idx);
	glPopMatrix ();
		
	int viewport[4];
	unsigned char pixel[3];
	pixel[0] = pixel[1] = pixel[2] =  0xFF;
	
	glGetIntegerv(GL_VIEWPORT, viewport);

	map<unsigned int, unsigned int> picked;
	
	for(unsigned int i = 0; i < wPick*hPick-1 ; i++)
	{
		glReadPixels( xx-2+i, viewport[3] - yy - 2+i, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);
		unsigned int pIdx = (pixel[0] << 16) + (pixel[1] << 8) + (pixel[2] );
		if( pixel[0] != 255 && pixel[1] != 255 &&  pixel[2] != 255)
		{
			picked[pIdx] = pIdx;
		}
	}
	//let displayPicked key value = print_int value ; print_endline " <- picked (Hashtbl)" in Hashtbl.iter displayPicked picked;
	vector<Color>& colors = mshMng.getPointsColor("testMesh");
	for(map<unsigned int, unsigned int>::iterator pIt = picked.begin() ; pIt != picked.end() ; pIt++)
	{
		cout << "picked node " << pIt->first << endl;
		colors[(pIt->first)].r = 255 - colors[(pIt->first)].r;
		colors[(pIt->first)].g = 255 - colors[(pIt->first)].g;
		colors[(pIt->first)].b = 255 - colors[(pIt->first)].b;
		if(isPicked[pIt->first])
			isPicked[pIt->first] = false;
		else
			isPicked[pIt->first] = true;
 	}

	glEnable(GL_DITHER);
	glEnable(GL_TEXTURE_2D);
	display();//This may be avoid by reordering picking and display
	cout << "picking done" << endl;
}

//setMat (dum:string) () = glColor3ub ((!count land 0xFF0000) lsr 16) ((!count land 0xFF00)  lsr 8) (!count land 0xFF) ; count := !count + 1
void Viewer::setMatForPicking(void* idx)
{
	unsigned int color = *static_cast<unsigned int*>(idx);
	glColor3ub( ((color & 0xFF0000) >> 16), ((color & 0xFF00)  >> 8),  (color & 0xFF) );
	(*static_cast<unsigned int*>(idx))++;
};

void Viewer::setMatForPoint(void* pInfo)
{
	DrawPoints* drawInfo = static_cast<DrawPoints*>(pInfo);
	int nIdx = drawInfo->idx;
	Color c = drawInfo->This->mshMng.getPointsColor(drawInfo->meshName)[nIdx];

	glColor3ub(c.r, c.g, c.b);
	drawInfo->idx++;
};

void Viewer::display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity() ;

	double x = camPosMove.x;
	double y = camPosMove.y;
	double z = camPosMove.z;
	glTranslatef(x, y, z);
	x = camPos.x;
	y = camPos.y;
	z = camPos.z;
	glTranslatef(x, y, z);

	Vector3 axis = camQuatMove.log();
	glRotatef(axis.norm() * 180.0 / 3.1415, axis.x, axis.y, axis.z);
	axis = camQuat.log();
	glRotatef(axis.norm() * 180.0 / 3.1415, axis.x, axis.y, axis.z);

	glPushMatrix ();
	loadSkyBox();
	glEnable(GL_COLOR_MATERIAL);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	drawScene(&(Viewer::setMat));
	if( mode == EDIT)
	 {
		//then let count = ref 0 in
		//	let setMat meshLabel () =
		//		let colorArray = mshMng#getPointsColor meshLabel
		//			in let r,g,b = !colorArray.(!count)
		//		in count := !count + 1 ; glColor3ub r g b in 
		DrawPoints drawInfo;
		drawInfo.This = this;
		drawInfo.meshName = string("testMesh");
		drawInfo.idx = 0;
		drawScenePoints(&(Viewer::setMatForPoint), &drawInfo);
	}
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glColor3ub(255, 255, 255);
	glPopMatrix();
}	

void Viewer::run(void)
{
	unsigned int toto = 0;
	while(isRunning)
	{
		frameStart();
		handleEvent() ;
		if( isActive)
		{
			
			// Let the gui perform it's logic (like handle input)
			gui->logic();
			// Draw the gui
			gui->draw();
			display ();
			gui->draw();
			SDL_GL_SwapBuffers();
		}
		frameEnd() ;
	}
}

void Viewer::start(void)
{
	run();
	cout << "stop" << endl;
}



void Baiji::keyCallback(SDL_KeyboardEvent k)
{
	if(k.state = SDL_PRESSED)
	{
		switch(k.keysym.sym)
		{
			case SDLK_q:
				isRunning = false;
				break;
			case SDLK_p:
				for(std::map<unsigned int, bool>::iterator pIt = isPicked.begin() ; pIt != isPicked.end() ; pIt++)
					if(pIt->second)
						cout << "<node idx=\"" << pIt->first << "\"/>" <<endl;
				break;
			case SDLK_LEFT:
				camPos.x += 0.1;
				break;
			case SDLK_RIGHT:
				 camPos.x -= 0.1;
				 break;
			case SDLK_UP:
				camPos.y -= 0.1;
				break;
			case SDLK_DOWN:
				camPos.y += 0.1;
				break;
			case SDLK_TAB:
				 if(mode == OBJECT)
				 {
					mode = EDIT;
				}
				else
					mode = OBJECT;
				break;
		   default:
			;
		}
	}
}

MeshUtils::Vector3 Baiji::unProject(const int x, const int y)
{
	double xx = xMin +  (float )x / (float)width * (xMax - xMin );
	double yy = -yMin -  (float)y / (float)height * (yMax - yMin );
	return Vector3(xx, yy , 0.1);
}

void Baiji::rotateCam(const int x, const int y)
{
	Vector3 sphereStop = unProject (x, y);
	Vector3 axis = (sphereStart ^ sphereStop);
	axis.normalize();
	Vector3 startN = sphereStart;
	Vector3 stopN = sphereStop;
	startN.normalize();
	stopN.normalize();
	double c = startN*stopN;
	double s = (startN ^stopN).norm();
	double alpha = asin(s);
	setCamQuatMove(Quaternion(axis, alpha));
}

void Baiji::zoomCam(const int x, const int y)
{
	int zoomStop = y;
	setCamPosMove (Vector3(0.0, 0.0, (float)(zoomStop-zoomStart) / (float)height * camPos.z));
}

void Baiji::mouseCallback(SDL_MouseButtonEvent b, bool up)
{
	switch(b.button)
	{
		case SDL_BUTTON_MIDDLE :
			if( picking)
			{
				if(mode == OBJECT)
					pickObject(b.x,b.y);
				else // EDIT Mode
					pickEdit(b.x, b.y);
			}
			picking = not picking;
			break;
		case SDL_BUTTON_RIGHT :
			if(up)
				zooming = false;
			else
				zooming = true;
			if(zooming)
				zoomStart = b.y;
			else
			{
				setCamPos(camPos + camPosMove);
					camPosMove.reset();
			}
			break;
		case SDL_BUTTON_LEFT :
			if(up)
				rotating = false;
			else
				rotating = true;
			if(rotating)
			{
				sphereStart = unProject(b.x, b.y);
			}
			else
			{
				setCamQuat(camQuatMove * camQuat) ;
				camQuatMove.reset();
			}
			break;
		case SDL_BUTTON_WHEELUP:
			break;
		case SDL_BUTTON_WHEELDOWN:
			break;
		default:
			cout << "Unknown mouse event" << endl;
	}
}

void Baiji::motionCallback(SDL_MouseMotionEvent m)
{
	if(rotating)
		rotateCam(m.x, m.y);
	if(zooming)
		zoomCam(m.x, m.y);
}
	/********************************* Inherited methods ***********************************/
void Baiji::frameStart(void)
{
	//let r = Hashtbl.find nodes "rootNode" and p = new vector ~xx:(0.0000*.step)() in !r#setPosition p ; step <- step+.1.0
}
void Baiji::frameEnd()
{}

void Baiji::handleEvent(void)
{
	static bool consoleOn = false;
	SDL_Event event;
	SDL_WaitEvent(&event);
	if(not console->isEnabled())
	switch(event.type)
	{
		case SDL_MOUSEBUTTONUP:
			mouseCallback(event.button, true);
			break;
		case SDL_MOUSEBUTTONDOWN:
			mouseCallback(event.button, false);
			break;
		
		case SDL_MOUSEMOTION:
			motionCallback(event.motion);
			break;
		
		case SDL_KEYDOWN:
			keyCallback(event.key);
			break;
		
		default:
			break;
	}
	switch(event.type)
	{
		case SDL_KEYDOWN:
			if(event.key.keysym.sym == SDLK_ESCAPE)
			{
				console->setEnabled(consoleOn);
				console->setVisible(consoleOn);
				consoleOn = !consoleOn;
			}
			break;
		case SDL_ACTIVEEVENT:
			if(event.active.gain == 1)
				isActive = true;
			else
				isActive = false;
			break;
		case SDL_QUIT:
			isRunning = false;
			break;
		default:
			break;
	}
	 input->pushInput(event);

}

void Baiji::addMesh(Mesh& msh, string name)
{
	mshMng.addMesh(msh, name);
}

Entity& Baiji::createEntity(string name, string mshName, string matName)
{
	return scnMng.createEntity(name, mshName,  matName);
}

// Should be generalized for any node, not only root
void Baiji::linkEntityToRoot(string name)
{
	scnMng.getRoot().linkEntity(name);
}
void Baiji::buildScene(void)
{
	nodes["rootNode"] = &(scnMng.getRoot());
	setCamPos(Vector3(0.0, 0.0, -5.0));
}
